﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.IO;
using System;

/// <summary>
/// 热更新脚本
/// 1、加载服务器版本文件
/// 2、加载本地版本文件
/// 3、对比得到需要更新的文件
/// 4、更新文件
/// </summary>
public class HotUpdate : MonoBehaviour {
    public static string LOG_FLAG = "[HotUpdate][cxs]";

    public static HotUpdate Instance { get; private set; }

    /// <summary>
    /// 热更新数据存储用到的类结构
    /// </summary>
    public class ABInfo
    {
        public string name;
        /// <summary>
        /// 路径,基于配置所在路径的相对路径
        /// 如Bytes/AI.bytes
        /// </summary>
        public string path;
        /// <summary>
        /// 版本号
        /// </summary>
        public float version;
        /// <summary>
        /// 大小
        /// </summary>
        public float size;

        public string ServerPath { get { return HotUpdateCfg.ServerABBase + path; } }

        public byte[] Data { get; private set; }

        public void LoadData(byte[] source)
        {
            if (source == null) return;

            Data=new byte[source.Length];
            Array.Copy(source,Data,source.Length);
        }
    }



    /// <summary>
    /// 本地大版本文件号
    /// </summary>
    private float m_LocalBigVersion=0;

    /// <summary>
    /// 服务器上大版本文件号
    /// </summary>
    private float m_ServerBigVersion=0;

    /// <summary>
    /// 服务器上版本信息字典
    /// </summary>
    private Dictionary<string, ABInfo> serverVerInfo=new Dictionary<string,ABInfo>();

    /// <summary>
    /// 本地版本信息字典
    /// </summary>
    private Dictionary<string, ABInfo> localVerInfo=new Dictionary<string,ABInfo>();

    /// <summary>
    /// 服务器上的版本文件缓存，在更新完文件之后再把这个缓存写入本地。
    /// </summary>
    private byte[] m_temserver_versionfile;

    /// <summary>
    /// 服务器上的大版本文件，在更新完之后保存到本地
    /// </summary>
    private byte[] m_BigVersionByte;


    /// <summary>
    /// 需要更新的资源列表
    /// </summary>
    private List<ABInfo> needUpInfo;

    /// <summary>
    /// 更新完成的资源
    /// </summary>
    public List<ABInfo> FinishUpdateInfo { get; private set; }


    /// <summary>
    /// 更新资源后的回调
    /// </summary>
    /// <param name="www"></param>
    public delegate void HandleFinishDownload(ABInfo data, WWW www);

    /// <summary>
    /// 加载完一个版本表后的回调
    /// </summary>
    public delegate void LoadXmlOver();

    /// <summary>
    /// 更新之后的回调
    /// </summary>
    private System.Action m_UpdateCallBack;


    void Awake()
    {
        Instance = this;
        FinishUpdateInfo = new List<ABInfo>();
    }

    /// <summary>
    /// 对比分析资源的版本并更新资源
    /// </summary>
    /// <returns></returns>
    private IEnumerator UpdateResources()
    {
        yield return StartCoroutine(HotUpdateTools.ClientWWWLoad(HotUpdateCfg.VersionFileName, LoadClientVersionXmlCallBack));//先加载本地的配置文件

        yield return StartCoroutine(HotUpdateTools.ServerWWWLoad(HotUpdateCfg.ServerABBase + HotUpdateCfg.VersionFileName, LoadServerVersionXmlCallBack));

        VersionLoadOver();
    }

    /// <summary>
    /// 比对更新资源
    /// </summary>
    /// <returns></returns>
    public IEnumerator CheckResourcesUpdate(System.Action updatecallback)
    {
        m_UpdateCallBack = updatecallback;

        FinishUpdateInfo.Clear();
        m_LocalBigVersion = 0;
        m_ServerBigVersion = 0;

        yield return StartCoroutine(HotUpdateTools.ClientWWWLoad(HotUpdateCfg.BigVersionFileName, LoadClientBigVersionCallBack));//先加载本地的大版本配置文件
 
        yield return StartCoroutine(HotUpdateTools.ServerWWWLoad(HotUpdateCfg.ServerABBase + HotUpdateCfg.BigVersionFileName, LoadServerBigVersionCallBack));//加载服务器的大版本文件

        Debug.Log(LOG_FLAG+"大版本版本号 客户端:" + m_LocalBigVersion + " 服务端:" + m_ServerBigVersion);

        //暂时不用对比
        if(m_LocalBigVersion< m_ServerBigVersion)
        {
            yield return UpdateResources();
        }
        else
        {
            //Debug.Log(LOG_FLAG+"没有热更新的数据");
            if (updatecallback != null)
                updatecallback();
        }
    }


    /// <summary>
    /// 版本配置加载完之后
    /// </summary>
    void VersionLoadOver()
    {
        CheckXml();
        if (AnaUpdateInfo() == false)
        {
            FinishUpdate();
            return;
        }
        UpdateLocalFile();
    }

    #region 分析对比大版本文件
    /// <summary>
    /// 加载本地的大版本文件
    /// </summary>
    /// <param name="bundle"></param>
    private void LoadClientBigVersionCallBack(WWW bundle)
    {
        if (bundle != null && bundle.size != 0)
        {
            m_LocalBigVersion = float.Parse(bundle.text);    
            bundle.Dispose();
        }
    }


    /// <summary>
    /// 加载服务器的大版本文件
    /// </summary>
    /// <param name="bundle"></param>
    private void LoadServerBigVersionCallBack(WWW bundle)
    {
        if (bundle != null && bundle.size != 0)
        {
            m_BigVersionByte = new byte[bundle.bytes.Length];
            Array.Copy(bundle.bytes, m_BigVersionByte, bundle.bytes.Length);
            m_ServerBigVersion = float.Parse(bundle.text);
            bundle.Dispose();
        }
    }
    #endregion

    #region 对比资源版本文件的逻辑

    /// <summary>
    /// 分析此次更新的数据 包括总大小 资源路径等
    /// 有更新返回true 否则返回false
    /// </summary>
    bool AnaUpdateInfo()
    {
        float sumSize = 0;
        if (needUpInfo != null)
        {
            for (int i = 0; i < needUpInfo.Count; ++i)
            {
                sumSize = sumSize + needUpInfo[i].size;
            }
        }
        else
        {

        }
        return sumSize > 0;
    }

    /// <summary>
    /// 对比xml文件
    /// </summary>
    public void CheckXml()
    {
        if (serverVerInfo == null || localVerInfo == null)
        {
            return;
        }
        if (needUpInfo == null)
            needUpInfo = new List<ABInfo>();
        needUpInfo.Clear();

        foreach (string key in serverVerInfo.Keys)
        {
            if (localVerInfo.ContainsKey(key))
            {
                if (serverVerInfo[key].version > localVerInfo[key].version)
                {
                    needUpInfo.Add(serverVerInfo[key]);
                    Debug.Log(LOG_FLAG + key + " :服务器版本（" + serverVerInfo[key].version + "） |客户端版本（" + localVerInfo[key].version + "）(需要更新)");
                }
            }
            else
            {
                needUpInfo.Add(serverVerInfo[key]);
                Debug.Log(LOG_FLAG + key + "( 新加的配置，需要更新)");
            }
        }
        if (needUpInfo.Count >= 2)
        {
            Debug.Log(LOG_FLAG + "需要更新的配置太多了，最好直接打到包里，要不然会第一次登陆会加载很久的！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！cxs2016-10-19");
        }
    }

    /// <summary>
    /// 加载本地的xml文件
    /// </summary>
    /// <param name="bundle"></param>
    private void LoadClientVersionXmlCallBack(WWW bundle)
    {
        if (bundle != null && bundle.size != 0)
        {
            XmlDocument xmlD = new XmlDocument();
            string content = HotUpdateTools.BytesToString(bundle.bytes);
            xmlD.LoadXml(content);
            SaveXmlToDir(xmlD, localVerInfo);
            bundle.Dispose();
        }
    }

    /// <summary>
    /// 加载服务器版本文件的返回
    /// </summary>
    /// <param name="bundle"></param>
    private void LoadServerVersionXmlCallBack(WWW bundle)
    {
        if (bundle != null && bundle.size != 0)
        {
            XmlDocument xmlD = new XmlDocument();
            string content = HotUpdateTools.BytesToString(bundle.bytes);
            m_temserver_versionfile = new byte[bundle.bytes.Length];
            Array.Copy(bundle.bytes, m_temserver_versionfile, bundle.bytes.Length);

            Debug.Log(content);
            xmlD.LoadXml(content);
            SaveXmlToDir(xmlD, serverVerInfo);
            bundle.Dispose();
        }
    }



    /// <summary>
    /// 把xml数据写入字典中
    /// </summary>
    private void SaveXmlToDir(XmlDocument doc,Dictionary<string,ABInfo> dir)
    {
        if (doc == null) return;
        if (dir == null)
            dir = new Dictionary<string, ABInfo>();
        dir.Clear();

        XmlNode root = doc.SelectSingleNode("Assets");
        CustomXmlNode CustomRoot = new CustomXmlNode(root);
        List<CustomXmlNode> child = CustomRoot.Children; 
        for(int i=0;i<child.Count;++i)
        {
            LoadKindOfFile(child[i],dir);
        }      
    }

    private void LoadKindOfFile(CustomXmlNode CustomRoot, Dictionary<string, ABInfo> dir)
    {
        if (CustomRoot == null) return;
        string basedir = CustomRoot.CurrentNode.Name + "/";
        for(int i=0;i<CustomRoot.Children.Count;++i)
        {
            CustomXmlNode child = CustomRoot.Children[i];
            ABInfo tem = new ABInfo();
            tem.name = child.GetAttribute("Name");
            tem.path = basedir + tem.name;
            tem.version = float.Parse(child.GetAttribute("Version"));
            tem.size = int.Parse(child.GetAttribute("Size"));
            dir.Add(tem.name,tem);
        }
    }

    #endregion

    #region 更新资源
    /// <summary>
    /// 更新本地资源以及配置文件
    /// </summary>
    void UpdateLocalFile()
    {
        if (needUpInfo != null && needUpInfo.Count > 0)
        {
            ABInfo firt = needUpInfo[0];
            needUpInfo.RemoveAt(0);
            FinishUpdateInfo.Add(firt);
            Debug.Log(LOG_FLAG+"Start update:" + firt.name);
            StartCoroutine(LoadAsset(firt, FinishUpdateOne));
        }
        else
        {
            //更新完毕
            FinishUpdate(true);
        }
    }

    /// <summary>
    /// 更新完一个资源后
    /// </summary>
    /// <param name="path"></param>
    /// <param name="w"></param>
    void FinishUpdateOne(ABInfo data, WWW w)
    {
        HotUpdateTools.ReplaceLocalRes(data.path, w.bytes);
        data.LoadData(w.bytes);
        Debug.Log(LOG_FLAG+"Finish update:" + data.name);
        w.Dispose();
        w = null;
        UpdateLocalFile();
    }

    /// <summary>
    /// 资源更新完之后的操作
    /// </summary>
    void FinishUpdate(bool isUpdate = false)
    {
        string result = "没有最新的配置需要更新";
        if (isUpdate == true)//有更新
        {
            HotUpdateTools.ReplaceLocalRes(HotUpdateCfg.VersionFileName, m_temserver_versionfile);//替换版本文件
            result = "有最新的数据需要更新";
        }
        HotUpdateTools.ReplaceLocalRes(HotUpdateCfg.BigVersionFileName, m_BigVersionByte);//替换版本文件
        Debug.Log(LOG_FLAG+ result + "并且完成了!");
        if (m_UpdateCallBack != null)
            m_UpdateCallBack();
    }


    IEnumerator LoadAsset(ABInfo data, HandleFinishDownload finish)
    {
        WWW www = new WWW(data.ServerPath);
        yield return www;
        if (finish != null)
        {
            finish(data, www);
        }
        www.Dispose();
    }
    #endregion

    #region 外部函数

    public void LoadBytes()
    {

    }

    #endregion

}
